/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/* Generated By:JJTree: Do not edit this line. AstValue.java */

// package org.apache.el.parser;
package com.gdevelop.gwtxp.rebind.el;


import com.google.gwt.core.ext.typeinfo.JClassType;

import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JType;

import com.google.gwt.core.ext.typeinfo.NotFoundException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author Jacob Hookom [jacob@hookom.net]
 * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: markt $
 */
public final class AstValue extends SimpleNode {

    protected static final boolean COERCE_TO_ZERO =
        Boolean.valueOf(System.getProperty(
                "org.apache.el.parser.COERCE_TO_ZERO", "true")).booleanValue();
    
    protected static class Target {
        protected Object base;

        protected Object property;
    }

    public AstValue(int id) {
        super(id);
    }

    private boolean isAssignable(Object value, Class<?> targetClass) {
        if (targetClass == null) {
            return false;
        } else if (value != null && targetClass.isPrimitive()) {
            return false;
        } else if (value != null && !targetClass.isInstance(value)) {
            return false;
        }
        return true;
    }


  private JClassType cachedReturnType = null;
  private boolean isStaticValue;
  public JClassType getReturnType(ELParserContext ctx) throws ELException, NotFoundException{
    isStaticValue = false;
    JClassType returnType = this.children[0].getReturnType(ctx);
    if (ctx.isGWTSpecialClasses(returnType)){
      isStaticValue = true;
    }
    for (int i=1; i<this.jjtGetNumChildren(); i++){
      if (this.children[i] instanceof AstBracketSuffix){
        returnType = ctx.autoboxed(returnType.isArray().getComponentType());
        continue;
      }
      
      String methodName;
      if (ctx.isGWTSpecialClasses(returnType)){
        methodName = this.children[i].getImage();
      }else{
        methodName = ctx.getGetMethodName(this.children[i].getImage());
      }
      //x JMethod method = returnType.getMethod(methodName, new JType[0]);
      JMethod method = ELParserContext.getMethod(returnType, methodName, new JType[0]);
      returnType = ctx.autoboxed(method.getReturnType());
      
      if (ctx.isGWTSpecialClasses(returnType)){
        isStaticValue = true;
      }
    }
    
    return cachedReturnType = returnType;
  }
  
  public boolean isStaticValue(ELParserContext ctx) throws ELException, NotFoundException{
    if (cachedReturnType == null){
      getReturnType(ctx);
    }
    return isStaticValue;
  }
  
  public boolean isSimpleExpression(ELParserContext ctx) throws ELException, NotFoundException{
    return true;
  }

  public String generateModelObservaleValue(ELParserContext ctx) throws ELException, NotFoundException {
    JClassType[] types = new JClassType[children.length];
    types[0] = this.children[0].getReturnType(ctx);
    for (int i=1; i<children.length; i++){
      String methodName = ctx.getGetMethodName(children[i].getImage());
      //x types[i] = ctx.autoboxed(types[i-1].getMethod(methodName, new JType[0]).getReturnType());
      types[i] = ctx.autoboxed(ELParserContext.getMethod(types[i-1], methodName, new JType[0]).getReturnType());
    }
    
    StringBuffer sb = new StringBuffer();
    for (int i=this.children.length-2; i>=0; i--){
      sb.append("new " + types[i].getQualifiedSourceName() + "ObservableValue(");
      ctx.addObservableBean(types[i].getQualifiedSourceName());
    }
    StringBuffer bean = new StringBuffer();
    for (int i=0; i<this.children.length-1; i++){
      if (i==0){
        sb.append("null, ");
      }else{
        sb.append(", ");
      }
      if ((i==0) && (ctx.getLocalBeanType(children[i].getImage()) != null)){
        bean.append(children[i].getImage());
      }else{
        if (i==0){
          bean.append("__this__");
        }
        bean.append(".");
        bean.append(ctx.getGetMethodName(children[i].getImage()));
        bean.append("()");
      }
      sb.append(bean);
      sb.append(", \"");
      sb.append(this.children[i+1].getImage());
      sb.append("\")");
    }

    return sb.toString();
  }
  
  public String generateGetExpression(ELParserContext ctx) throws ELException, NotFoundException{
    JClassType[] types = new JClassType[children.length];
    types[0] = this.children[0].getReturnType(ctx);
    for (int i=1; i<children.length; i++){
      if (this.children[i] instanceof AstBracketSuffix){
        types[i] = ctx.autoboxed(types[i-1].isArray().getComponentType());
        continue;
      }

      String methodName;
      if (ctx.isGWTSpecialClasses(types[i-1])){
        methodName = children[i].getImage();
      }else{
        methodName = ctx.getGetMethodName(children[i].getImage());
      }
      //x types[i] = ctx.autoboxed(types[i-1].getMethod(methodName, new JType[0]).getReturnType());
      types[i] = ctx.autoboxed(ELParserContext.getMethod(types[i-1], methodName, new JType[0]).getReturnType());
    }
    
    StringBuffer sb = new StringBuffer();
    for (int i=0; i<this.children.length; i++){
      if (i==0){
        sb.append("__this__");
      }

      if (this.children[i] instanceof AstBracketSuffix){
        types[i] = ctx.autoboxed(types[i-1].isArray().getComponentType());
        sb.append("[");
        sb.append(children[i].generateGetExpression(ctx));
        sb.append("]");
        continue;
      }

      sb.append(".");
      String methodName;
      if ((i>0) && (ctx.isGWTSpecialClasses(types[i-1]))){
        methodName = children[i].getImage();
      }else{
        methodName = ctx.getGetMethodName(children[i].getImage());
      }
      sb.append(methodName);
      sb.append("()");
    }
    
    return sb.toString();
  }
  
  public String generateMethodBinding(ELParserContext ctx) throws ELException, NotFoundException{
    JClassType[] types = new JClassType[children.length];
    types[0] = this.children[0].getReturnType(ctx);
    for (int i=1; i<children.length-1; i++){
      String methodName = ctx.getGetMethodName(children[i].getImage());
      // types[i] = ctx.autoboxed(types[i-1].getMethod(methodName, new JType[0]).getReturnType());
      types[i] = ctx.autoboxed(ELParserContext.getMethod(types[i-1], methodName, new JType[0]).getReturnType());
    }
    
    StringBuffer sb = new StringBuffer();
    for (int i=0; i<this.children.length; i++){
      if (i==0){
        sb.append("__this__");
      }
      if (i==this.children.length-1){
        Node node = children[i];
        JMethod m = null;
        // Find zero parameter method
        try{
          m = ELParserContext.getMethod(types[i-1], node.getImage(), new JType[0]);
        }catch(NotFoundException e){
          e.getMessage();
        }
        if (m != null){
          sb.append(".");
          sb.append(node.getImage());
          sb.append("();");
        }
        if (m == null){
          // Find method with one parameter type of GwtEvent
          for (JMethod m2 : types[i-1].getMethods()){
            if ((m2.getName().equals(node.getImage())) && (m2.getParameters().length == 1)){
              JClassType paramType = ctx.autoboxed(m2.getParameters()[0].getType());
              if (ctx.TYPE_GwtEvent.isAssignableFrom(paramType)){
                sb.append(".");
                sb.append(node.getImage());
                sb.append("(event);");
                m = m2;
                break;
              }
            }
          }
        }
        if (m == null){
          throw new NotFoundException("Could not generate MethodBinding for: " + node.getImage());
        }
      }else{
        sb.append(".");
        sb.append(ctx.getGetMethodName(children[i].getImage()));
        sb.append("()");
      }
    }
    
    return sb.toString();
  }

  /**
   *Moved to GenerationContext class
   * @param type
   * @param methodName
   * @param params
   * @return
   * @throws NotFoundException
   */
  private JMethod _getMethod(JClassType type, String methodName, JType[] params) throws NotFoundException {
    try{
      return type.getMethod(methodName, params);
    }catch(NotFoundException e){
      // Try to getMethod on Superclass
      try{
        if (type.getSuperclass() != null){
          return _getMethod(type.getSuperclass(), methodName, params);
        }
      }catch (NotFoundException e2){
        e2.getMessage();
      }

      // Try to getMethod on ImplementedInterfaces
      for (JClassType superInterface : type.getImplementedInterfaces()){
        try{
          return _getMethod(superInterface, methodName, params);
        }catch (NotFoundException e2){
          e2.getMessage();
        }
      }
      throw e;
    }
  }
}
